//==============================================================================
//
//              TextWindow - the text window class in OFC
//
//               Copyright (C) 2003  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2003-10-31 08:29:05 $ $Revision: 1.6 $
//
//==============================================================================

#include "ofc/DTextWindow.h"

#include "ofc/DList.h"

///
/// #include "ofc/config.h"
///
/// #include <ncurses.h>
/// #include <stdlib.h>
///
/// // The Text color class stores colors for the Text Window class. 
///
/// @interface DTextColor : Object
/// {
/// @private
///   short          _fg;
///   short          _bg;
///   short          _pair;
/// } 
///
/// // the colors
///
/// #define DTC_BLACK   COLOR_BLACK
/// #define DTC_RED     COLOR_RED
/// #define DTC_GREEN   COLOR_GREEN
/// #define DTC_YELLOW  COLOR_YELLOW
/// #define DTC_BLUE    COLOR_BLUE
/// #define DTC_MAGENTA COLOR_MAGENTA
/// #define DTC_CYAN    COLOR_CYAN
/// #define DTC_WHITE   COLOR_WHITE
///

@implementation DTextColor

static unsigned _nextPair = 1;

//
// Initialise a new text color
//
// @param fg              the foreground color
// @param bg              the background color
//
// @return the object
//

- (DTextColor *) init :(short) fg :(short) bg
{
  [super init];

  _fg = fg;
  _bg = bg;

  _pair = _nextPair++;

  if (has_colors())
    init_pair(_pair, _fg, _bg);
  
  return self;
}



//// Members

//
// Get the color
//
// @return the color
//

- (short) color
{
  return _pair;
}

@end



///
/// // The Text Window class implements a text windows. On Linux the ncurses 
/// // library is used to implement it.
///
/// @interface DTextWindow : Object
/// {
/// @private
///   WINDOW        *_window;          // the window handle
///   int            _attr;            // the current attributes
///   short          _color;           // the current color
/// }
///
/// // Attributes
///
/// #define DTW_NORMAL    A_NORMAL	  // Normal display (no highlight)
///	#define DTW_STANDOUT  A_STANDOUT	// Best highlighting mode of the terminal.
///	#define DTW_UNDERLINE A_UNDERLINE	// Underlining
///	#define DTW_REVERSE   A_REVERSE	  // Reverse video
///	#define DTW_BLINK     A_BLINK		  // Blinking
/// #define DTW_BOLD      A_BOLD      // Bold
///

@implementation DTextWindow

static DList       *_windows = nil;
static DTextWindow *_root    = nil; // Temp DList.getAt

static void doExit()
{
  [DTextWindow close];
}

//// Class methods

//
// Open the text windowing system
//
// @return the root window (or nil)
//

+ (DTextWindow *) open
{
  if (_root == nil)
  {
    WINDOW *window = initscr();
    
    if (window == NULL)
      /* error message */ return nil;

    atexit(doExit);
    
    if (has_colors())
      start_color();
      
    raw();
    noecho();
    
    _windows = [DList       alloc];
    _root    = [DTextWindow alloc];
    
    [_windows init]; // retain];
    [_root    init :window];
  }
  
  return _root;
}

//
// Close the text windowing system
//

+ (void) close
{
  if (_root != nil)
  {
    // iterate over list from end to start, free all text windows
    [_windows free]; // release];

    echo();
    nocbreak();

    endwin();

    _root = nil;
  }
}



//// Constructors

//
// Initialises a new default, full screen window
//
// @return the object
//

- (DTextWindow *) init 
{
  [self init :0 :0 :0 :0];

  return self;
}

//
// Initialise a new text window
//
// @param x               the start column
// @param y               the start row
// @param width           the width
// @param height          the height
//
// @return the object
//

- (DTextWindow *) init :(int) x :(int) y :(int) width :(int) height
{
  [super init];

  if (_windows == nil)
    /* error message */ return nil;

   _window = newwin(height, width, x, y);

  keypad (_window, TRUE);
  nodelay(_window, TRUE);

  _attr  = DTW_NORMAL;
  _color = 0;
  
  if (_window != NULL)
    [_windows append :self];

  return self;
}

//
// Initialise a text window with a window handle (private)
//
// @param window        the window handle
//
// @return the object
//

- (DTextWindow *) init :(WINDOW *) window
{
  [super init];

  if (_windows == nil)
    /* error message */ return nil;

  _window = window;

  keypad (_window, TRUE);
  nodelay(_window, TRUE);

  _attr  = DTW_NORMAL;
  _color = 0;
  
  if (_window != NULL)
    [_windows append :self];

  return self;
}

//
// Copy constructor
//
// @return the object
//

// ToDo...



//// Deconstructor

//
// Free the object
//
// @return the object
//

- free
{
  [_windows remove :self];

  delwin(_window);
    
  [super free];

  return self;
}



//// Members

//
// Is the text window valid
//
// @return is it ?
//

- (BOOL) isValid
{
  return (_window != NULL);
}

//
// Get the width of the window
//
// @return the width
//

- (int) width
{
  int x,y;
  
  getmaxyx(_window, y, x);
  
  return x;
}

//
// Get the height of the window
//
// @return the height
//

- (int) height
{
  int x,y;

  getmaxyx(_window, y, x);

  return y;
}

//
// Get the current x position in the window
//
// @return the x position
//

- (int) x
{
  int x,y;

  getyx(_window, y, x);

  return x;
}

//
// Get the current y position in the window
//
// @return the y position
//

- (int) y
{
  int x,y;

  getyx(_window, y, x);
  
  return y;
}



//// Output methods

//
// Move the cursor to a lokation
//
// @param x             the x lokation
// @param y             the y lokation
//
// @return the object
//

- (DTextWindow *) gotoxy :(int) x :(int) y
{
  wmove(_window, y, x);
  
  return self;
}

//
// Write a character in the window (on the current location and in 
// the current attributes)
//
// @param ch            the character
//
// @return the object
//

- (DTextWindow *) writeChar :(char) ch
{
  waddch(_window, ch);

  return self;
}


//
// Write a string in the window (on the current location and in
// the curretn attributes)
//
// @param str           the string
//
// @return the object
//

- (DTextWindow *) writeString :(const char *) str
{
  waddstr(_window, str);

  return self;
}

//
// Write a formatted string in the window (on the current location
// and in the current attributes)
//
// @param fmt           the format string
//
// @return the object
//

- (DTextWindow *) writeFormat :(char *) fmt,...
{
  va_list ap;
  
  va_start(ap, fmt);
  vwprintw(_window, fmt, ap);
  va_end(ap);

  return self;
}

//
// Clear till end of line
//
// @return the object
//

- (DTextWindow *) clearEol
{
  wclrtoeol(_window);

  return self;
}

//
// Clear the window
//
// @return the object
//

- (DTextWindow *) clear
{
  werase(_window);

  return self;
}

//
// Update the window after writing
//
// @return the object
//

- (DTextWindow *) update
{
  wrefresh(_window);
 
  return self;
}



// Input methods

//
// Read a character from the keyboard
//
// @return the read character (or -1 for no key)
//

- (int) readChar
{
  int ch = wgetch(_window);

  if (ch == ERR) ch = -1;

  return ch;
}



//// Attributes and colors

//
// Set the attributes
//
// @param attr          the attributes
//
// @return the object
//


- (DTextWindow *) attr :(int) attr
{
  _attr = attr;
  
  if (has_colors())
  {
    attr |= COLOR_PAIR(_color);
  }
   
  wattrset(_window, attr);

  return self;
}

//
// Get the current attributes
//
// @return the current attributes
//

- (int) attr
{
  return _attr;
}



//// Colors

//
// Set the current color
//
// @param color         the color to be used (<> nil)
//
// @return the object
//

- (DTextWindow *) color :(DTextColor *) color
{
  if (has_colors())
  {
    if (color != nil)
      _color = [color color];

    wattrset(_window, _attr | COLOR_PAIR(_color));
  }

  return self;
}

//// Tests

//
// Test the class
//

+ (void) test
{
  DTextWindow *root  = [DTextWindow open];
  DTextWindow *sub   = [DTextWindow alloc];
  DTextColor  *color = [DTextColor alloc];
  
  STARTTEST();

  TEST([root isValid]);

  TEST([[root gotoxy :10 :10] writeString :"Hello"] != nil);

  TEST([[root attr :DTW_BOLD] writeChar :'@'] != nil);

  TEST([color init :DTC_RED :DTC_WHITE] != nil);

  TEST([[root color :color] writeString :"Bye"] != nil);

  [[root gotoxy :10 :20] writeFormat :"Height:%d Width:%d", [root height], [root width]];

  TEST([root update] != nil);

  [root color :[color init :DTC_WHITE :DTC_BLACK]];
  
  [root gotoxy :1 :1];
  [root writeString :"Press any key:"];

  
  [sub init :5 :5 :5 :10];

  TEST([sub isValid]);

  [sub color :color];
  [sub writeString :"SubSubSubSub"];
  [sub update];

  while ([root readChar] == -1);
  
  [sub free];
  
  [[root clear] update]; 

  STOPTEST();

  [DTextWindow close];
}

@end

/*==========================================================================*/
